#include "httpsconnection.h"

httpsConnection::httpsConnection (boost::asio::io_service& io_service, boost::asio::ssl::context& context, boost::asio::ip::tcp::resolver::iterator iendpoint_iterator, connection* iproxy_conn)
    : ssocket_(io_service, context)
{
//    endpoint_iterator = iendpoint_iterator;
    proxy_conn = iproxy_conn;

    std::cout << "httpsConnection" << std::endl;
    boost::asio::ip::tcp::endpoint endpoint = *iendpoint_iterator;
    ssocket_.lowest_layer().async_connect(endpoint,
        boost::bind(&httpsConnection::handle_connect, this,
          boost::asio::placeholders::error, ++iendpoint_iterator));

}

void httpsConnection::handle_connect(const boost::system::error_code& error,
    boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
    std::cout << "rerere" << std::endl;
    if (!error) {
        std::cout << "handle_connect" << std::endl;
        ssocket_.async_handshake(boost::asio::ssl::stream_base::client,
            boost::bind(&httpsConnection::handle_handshake, this,
              boost::asio::placeholders::error));
    } else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator()) {
        std::cout << "in endpoint_iterator != boost::asio::ip::tcp::resolver::iterator" << std::endl;
        ssocket_.lowest_layer().close();
        boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
        ssocket_.lowest_layer().async_connect(endpoint,
            boost::bind(&httpsConnection::handle_connect, this,
              boost::asio::placeholders::error, ++endpoint_iterator));
    } else {
        std::cout << "Connect failed: " << error << "\n";
        shutdown();
    }
}
//void httpsConnection::handle_handshake(const boost::system::error_code& error)
//{
//  if (!error)
//  {
//      std::cout << "handle_handshake" << std::endl;
//      start_write_to_server();
//  }
//  else
//  {
//    std::cout << "Handshake failed: " << error << "\n";
//  }
//}

void httpsConnection::handle_handshake(const boost::system::error_code& error)
{
  if (!error)
  {
//      std::cout << "Enter message: ";
//      std::cin.getline(request_, max_length);
    std::stringstream mrequest_;
    mrequest_ << "GET www.google.com:443 HTTP/1.1\r\n";
    mrequest_ << "Host: www.google.com\r\n";
    mrequest_ << "Accept-Encoding: *\r\n";
    mrequest_ << "\r\n";

//      size_t request_length = strlen(mrequest_);

    boost::asio::async_write(ssocket_,
        boost::asio::buffer(mrequest_.str(), mrequest_.str().length() ),
        boost::bind(&httpsConnection::handle_write, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }
  else
  {
    std::cout << "Handshake failed: " << error << "\n";
  }
}

void httpsConnection::handle_write(const boost::system::error_code& error,
    size_t bytes_transferred)
{
  if (!error)
  {
    boost::asio::async_read(ssocket_,
        boost::asio::buffer(reply_, bytes_transferred),
        boost::bind(&httpsConnection::handle_read, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }
  else
  {
    std::cout << "Write failed: " << error << "\n";
  }
}

void httpsConnection::handle_read(const boost::system::error_code& error,
    size_t bytes_transferred)
{
  if (!error)
  {
    std::cout << "Reply: ";
    std::cout.write(reply_, bytes_transferred);
    std::cout << "\n";
  }
  else
  {
    std::cout << "Read failed: " << error << "\n";
  }
}

/**
 * Start read data of request from browser
 *
 */
void httpsConnection::start() {
//  	std::cout << "start" << std::endl;
    fHeaders.clear();
    reqHeaders.clear();
    respHeaders.clear();
}

void httpsConnection::handle_handshake_noparam()
{
    start_write_to_server();
}


/**
 * Write data to the web-server
 *
 */
void httpsConnection::start_write_to_server() {
    std::cout << "start_write_to_server" << std::endl;
    fReq=proxy_conn->getfServer();
    fReq+=" ";
    fReq+=proxy_conn->getfNewUrl();
    fReq+=" HTTP/";
    fReq+="1.1";
//	fReq+=fReqVersion;
    fReq+="\r\n";
    fReq+=proxy_conn->getfHeader();
    std::cout << "Request: " << fReq << std::endl;
    ba::async_write(ssocket_, ba::buffer(fReq),
                    boost::bind(&httpsConnection::handle_server_write, this,
                                ba::placeholders::error,
                                ba::placeholders::bytes_transferred));

    fHeaders.clear();
}

/**
 * If successful, read the header that came from a web server
 *
 * @param err
 * @param len
 */
void httpsConnection::handle_server_write(const bs::error_code& err, size_t len) {
// 	std::cout << "handle_server_write. Error: " << err << ", len=" << len << std::endl;
    if(!err) {
        std::cout << "handle_server_write" << std::endl;
        handle_server_read_headers(bs::error_code(), 0);
    }else {
        shutdown();
    }
}

/**
 * Read header of data returned from the web-server
 *
 * @param err
 * @param len
 */
void httpsConnection::handle_server_read_headers(const bs::error_code& err, size_t len) {
// 	std::cout << "handle_server_read_headers. Error: " << err << ", len=" << len << std::endl;
    if(!err) {
        std::cout << "handle_server_read_headers" << std::endl;
        std::string::size_type idx;
        if(fHeaders.empty())
            fHeaders=std::string(sbuffer.data(),len);
        else
            fHeaders+=std::string(sbuffer.data(),len);
        idx=fHeaders.find("\r\n\r\n");
        if(idx == std::string::npos) { // going to read rest of headers
            std::cout << "nothing" << std::endl;

            async_read(ssocket_, ba::buffer(sbuffer), ba::transfer_at_least(1),
                       boost::bind(&httpsConnection::handle_server_read_headers,
                                   this,
                                   ba::placeholders::error,
                                   ba::placeholders::bytes_transferred));
        } else { // analyze headers
            std::cout << "Response: " << fHeaders << std::endl;
            RespReaded=len-idx-4;
            idx=fHeaders.find("\r\n");
            std::string respString=fHeaders.substr(0,idx);
            RespLen = -1;
            parseHeaders(fHeaders.substr(idx+2),respHeaders);
            std::string reqConnString="",respConnString="";

            std::string respVersion=respString.substr(respString.find("HTTP/")+5,3);

            headersMap::iterator it=respHeaders.find("Content-Length");
            if(it != respHeaders.end())
                RespLen=boost::lexical_cast<int>(it->second);
            it=respHeaders.find("Connection");
            if(it != respHeaders.end())
                respConnString=it->second;
            it=reqHeaders.find("Connection");
            if(it != reqHeaders.end())
                reqConnString=it->second;

            isPersistent=(
                ((fReqVersion == "1.1" && reqConnString != "close") ||
                 (fReqVersion == "1.0" && reqConnString == "keep-alive")) &&
                ((respVersion == "1.1" && respConnString != "close") ||
                 (respVersion == "1.0" && respConnString == "keep-alive")) &&
                RespLen != -1);
// 			std::cout << "RespLen: " << RespLen << " RespReaded: " << RespReaded
// 					  << " isPersist: " << isPersistent << std::endl;

            // sent data
            ba::async_write(proxy_conn->socket(), ba::buffer(fHeaders),
                            boost::bind(&httpsConnection::handle_browser_write,
                                        this,
                                        ba::placeholders::error,
                                        ba::placeholders::bytes_transferred));
        }
    } else {
        shutdown();
    }
}

/**
 * Writing data to the browser, are recieved from web-server
 *
 * @param err
 * @param len
 */
void httpsConnection::handle_browser_write(const bs::error_code& err, size_t len) {
//   	std::cout << "handle_browser_write. Error: " << err << " " << err.message()
//  			  << ", len=" << len << std::endl;
    if(!err) {
        if(!proxy_closed && (RespLen == -1 || RespReaded < RespLen))
            async_read(ssocket_, ba::buffer(sbuffer,len), ba::transfer_at_least(1),
                       boost::bind(&httpsConnection::handle_server_read_body,
                                   this,
                                   ba::placeholders::error,
                                   ba::placeholders::bytes_transferred));
        else {
//			shutdown();
            if(isPersistent && !proxy_closed) {
                std::cout << "Starting read headers from browser, as connection is persistent" << std::endl;
                start();
            }
        }
    } else {
        shutdown();
    }
}

/**
 * Reading data from a Web server, for the writing them to the browser
 *
 * @param err
 * @param len
 */
void httpsConnection::handle_server_read_body(const bs::error_code& err, size_t len) {
//   	std::cout << "handle_server_read_body. Error: " << err << " " << err.message()
//  			  << ", len=" << len << std::endl;
    if(!err || err == ba::error::eof) {
        RespReaded+=len;
// 		std::cout << "len=" << len << " resp_readed=" << RespReaded << " RespLen=" << RespLen<< std::endl;
        if(err == ba::error::eof)
            proxy_closed=true;
        ba::async_write(proxy_conn->socket(), ba::buffer(sbuffer,len),
                        boost::bind(&httpsConnection::handle_browser_write,
                                    this,
                                    ba::placeholders::error,
                                    ba::placeholders::bytes_transferred));
    } else {
        shutdown();
    }
}

void httpsConnection::shutdown() {
    ssocket_.lowest_layer().close();
    proxy_conn->socket().close();
}

void httpsConnection::parseHeaders(const std::string& h, headersMap& hm) {
    std::string str(h);
    std::string::size_type idx;
    std::string t;
    while((idx=str.find("\r\n")) != std::string::npos) {
        t=str.substr(0,idx);
        str.erase(0,idx+2);
        if(t == "")
            break;
        idx=t.find(": ");
        if(idx == std::string::npos) {
            std::cout << "Bad header line: " << t << std::endl;
            break;
        }
// 		std::cout << "Name: " << t.substr(0,idx)
// 				  << " Value: " << t.substr(idx+2) << std::endl;
        hm.insert(std::make_pair(t.substr(0,idx),t.substr(idx+2)));
    }
}
